package math;

import static math.ExpressionBuilder.exp;
import static math.ExpressionBuilder.ln;
import static math.ExpressionBuilder.mult;

public class Pow implements ArithmeticExpression {

  private ArithmeticExpression base;

  private ArithmeticExpression exponent;

  private Pow(ArithmeticExpression base, ArithmeticExpression exponent) {
    this.base = base;
    this.exponent = exponent;
  }

  public static ArithmeticExpression createPow(ArithmeticExpression base,
      ArithmeticExpression exponent) {
    return new Pow(base, exponent);
  }

  @Override
  public ArithmeticExpression derivate(Variable var) {
    return exp(mult(exponent, ln(base))).derivate(var);
  }

  @Override
  public int getPriority() {
    return 3;
  }

  @Override
  public boolean isOne() {
    return base.isOne();
  }

  @Override
  public boolean isZero() {
    return false;
  }

  @Override
  public ArithmeticExpression simplify() {
    return this;
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    if (base.getPriority() > getPriority())
      sb.append('(');
    sb.append(base.toString());
    if (base.getPriority() > getPriority())
      sb.append(')');
    sb.append("^");
    if (exponent.getPriority() > getPriority())
      sb.append('(');
    sb.append(exponent.toString());
    if (exponent.getPriority() > getPriority())
      sb.append(')');
    return sb.toString();
  }

  public ArithmeticExpression getBase() {
    return base;
  }
  
  public ArithmeticExpression getExponent() {
    return exponent;
  }
  
  @Override
  public boolean matches(Expression expr) {
    if (expr instanceof Pow) {
      Pow ePow = (Pow) expr;
      return base.matches(ePow.getBase()) && exponent.matches(ePow.getExponent());
    } else
      return false;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof Pow) {
      Pow pow2 = (Pow) obj;
      return pow2.base.equals(base) && pow2.exponent.equals(exponent);
    } else
      return false;
  }
  
  @Override
  public int countMembers() {
    return 1 + base.countMembers() + exponent.countMembers();
  }
  
  @Override
  public int hashCode() {
    return base.hashCode() ^ (exponent.hashCode() << 2) ^ exponent.hashCode();
  }

  @Override
  public <R> R accept(ExpressionVisitor<R> visitor) {
    return visitor.visitPow(this);
  }
}